<?php

namespace app\customer;

use app\consts\Keywords;
use app\exception\UnauthorizedException;
use app\exception\ValidationException;

/**
 * 业务请求对象，捕获请求参数并进行参数验证，如果失败抛出验证失败的异常，
 * 如果成功则将请求参数填充到对象本体。
 *
 * 具体的业务请继承此类，写好验证规则和具体的业务方法，并在控制器中注入，通过对业务方法的调用实现业务
 */
class FormRequest
{
    public function __construct()
    {
        //验证授权
        $this->authorizeOrFail();
        //验证参数
        $this->validateOrFail();
    }

    /**
     * 参数校验或者出错抛出异常
     * @return void
     * @throws ValidationException
     */
    protected function validateOrFail()
    {
        $validator = validator($this->data(),$this->rules(),$this->messages());
        if($validator->fails()){
            throw new ValidationException($validator->errors()->toArray());
        }
        //对象加载属性
        $this->load($validator->validated());
    }

    /**
     * 验证授权或者出错抛出异常
     * @return void
     * @throws UnauthorizedException
     */
    protected function authorizeOrFail()
    {
        $this->authorize() || throw new UnauthorizedException();
    }

    /**
     * 对象属性填充，根据cast进行属性类型的转换
     * @param array $attributes
     * @return void
     */
    protected function load(array $attributes)
    {
        $casts = $this->casts();
        foreach ($attributes as $attribute => $value) {
            property_exists($this, $attribute) && $this->{$attribute} = $this->castAttribute($value,$casts[$attribute]??null);
        }
    }

    /**
     * 访问权限验证
     * @return bool
     */
    public function authorize() :bool
    {
        return true;
    }

    /**
     * 获取数据
     * @return mixed|null
     */
    public function data()
    {
        return request()->all();
    }

    /**
     * 验证规则数组
     * @return array
     */
    public function rules() :array
    {
        return [];
    }

    /**
     * 验证消息提示
     * @return array
     */
    public function messages() :array
    {
        return [];
    }

    /**
     * 属性的转换规则
     * @return array
     */
    public function casts() :array
    {
        return [];
    }

    public function castAttribute($value, ?string $type)
    {
        if (is_null($value) || is_null($type)) {
            return $value;
        }
        switch ($type) {
            case 'integer':
                $value = (int) $value;
                break;
            case 'float':
                $value = (float) $value;
                break;
            case 'boolean':
                $value = (bool) $value;
                break;
            case 'array':
                $value = (array) $value;
            // no break
            case 'json':
                $option = !empty($param) ? (int) $param : JSON_UNESCAPED_UNICODE;
                $value  = json_encode($value, $option);
                break;
            case 'object':
                if (is_object($value)) {
                    $value = json_encode($value, JSON_FORCE_OBJECT);
                }
                break;
            case 'serialize':
                $value = serialize($value);
                break;
            default:
                if (is_object($value) && false !== strpos($type, '\\') && method_exists($value, '__toString')) {
                    // 对象类型
                    $value = $value->__toString();
                }
        }
        return $value;
    }

    /**
     * 获取用户id
     * @return int
     */
    public function uid() :int
    {
        return intval(request()->{Keywords::USER_ID});
    }

    /**
     * 获取网站语言参数
     * @return string
     */
    public function lang() :string
    {
        return locale();
    }
}